// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Library General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301,  USA

#include "partida.h"

/*
 *
 *	INICIALIZACIÓN JUEGO
 *
 *
 */
void Partida::InicializaJuego(int numero_jugadores)
{
	switch (numero_jugadores)
	{
		case 2:
			juego_actual.nroParciales = 4;
			juego_actual.nroJugadores = 2;
			control_entrada.bolas_por_box = 6;
			control_entrada.box1[3] = 0;
			control_entrada.box2[3] = 0;
			control_entrada.box3[3] = 6;
			control_entrada.box4[3] = 6;
			control_entrada.box5[3] = 0;
			control_entrada.box6[3] = 0;
			break;
			
		case 6:
			juego_actual.nroParciales = 6;
			juego_actual.nroJugadores = 6;
			control_entrada.bolas_por_box = 2;
			control_entrada.box1[3] = 2;
			control_entrada.box2[3] = 2;
			control_entrada.box3[3] = 2;
			control_entrada.box4[3] = 2;
			control_entrada.box5[3] = 2;
			control_entrada.box6[3] = 2;
			break;
	}

	// Inicializar puntuacion de todo el juego
	juego_actual.punt.azul = 0;
	juego_actual.punt.rojo = 0;

	for (int i = 0; i < 3; i++)
	{
		control_entrada.box1[i] = 0;
		control_entrada.box2[i] = 0;
		control_entrada.box3[i] = 0;
		control_entrada.box4[i] = 0;
		control_entrada.box5[i] = 0;
		control_entrada.box6[i] = 0;
	}
	// Inicializamos las estructuras del juego, todo a cero, menos los boxes
	//  de lanzamiento de la bola blanca
	juego_actual.parcial = new parcial_boccia[juego_actual.nroParciales];
	juego_actual.parcialActual = 0;
	
	for (int i = 0; i < juego_actual.nroParciales; i++)
	{
		juego_actual.parcial[i].bolas_lanzadas = 0;
		juego_actual.parcial[i].finalizado = 0;
		juego_actual.parcial[i].box_color = 0; // Si es cero, sabremos 
												// que no se ha tirado la blanca
		juego_actual.parcial[i].perdidas_azul = 0;
		juego_actual.parcial[i].perdidas_rojo = 0;
		if (juego_actual.nroJugadores == 6)
			juego_actual.parcial[i].box_bola_blanca = i+1;
		else
			if ((i+1)%2)
				juego_actual.parcial[i].box_bola_blanca = 3;
			else
				juego_actual.parcial[i].box_bola_blanca = 4;
				
		juego_actual.parcial[i].bola_blanca_en_campo = 0;
		// Inicializar puntuacion de cada parcial
		juego_actual.parcial[i].punt.azul = 0;
		juego_actual.parcial[i].punt.rojo = 0;
		
		
	}

	// Inicializamos la estructura de control del menú de juego

	for (int i = 0; i < 3; i++)
	{
		control_entrada.box1[i] = 0;
		control_entrada.box2[i] = 0;
		control_entrada.box3[i] = 0;
		control_entrada.box4[i] = 0;
		control_entrada.box5[i] = 0;
		control_entrada.box6[i] = 0;
	}

	ActualizaValoresEntrada();

}

void Partida::InicializaBolasEquipos()
{
	// Aquí, además de guardarlos en nuestros equipos y en el struct del parcial la bola blanca
	conjunto_bolas->Limpiar();
	
	ElementoDinamico* bola;
	
	bola = new ElementoDinamico(DUREZA_SEMI, "blanca");
	bola->CargarObjeto("bola_blanca.obj");
	conjunto_bolas->NuevaBola(bola);
	
	juego_actual.parcial[
						juego_actual.parcialActual
						].bola_blanca = bola;
	
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < control_entrada.box1[i]; j++)
			{
				
				bola = new ElementoDinamico(i, "rojo");
				bola->CargarObjeto("bola_roja.obj");
				equipo_rojo->AsignarBola(1, bola);
				conjunto_bolas->NuevaBola(bola);
			}
			
			
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < control_entrada.box2[i]; j++)
			{
				
				bola = new ElementoDinamico(i, "azul");
				bola->CargarObjeto("bola_azul.obj");
				equipo_azul->AsignarBola(2, bola);
				conjunto_bolas->NuevaBola(bola);
			}
			
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < control_entrada.box3[i]; j++)
			{
				bola = new ElementoDinamico(i, "rojo");
				bola->CargarObjeto("bola_roja.obj");
				equipo_rojo->AsignarBola(3, bola);
				conjunto_bolas->NuevaBola(bola);
			}
	
			
			
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < control_entrada.box4[i]; j++)
			{
				
				bola = new ElementoDinamico(i, "azul");
				bola->CargarObjeto("bola_azul.obj");
				equipo_azul->AsignarBola(4, bola);
				conjunto_bolas->NuevaBola(bola);
			}
			
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < control_entrada.box5[i]; j++)
			{
				
				bola = new ElementoDinamico(i, "rojo");
				bola->CargarObjeto("bola_roja.obj");
				equipo_rojo->AsignarBola(5, bola);
				conjunto_bolas->NuevaBola(bola);
			}
			
			
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < control_entrada.box6[i]; j++)
			{
				
				bola = new ElementoDinamico(i, "azul");
				bola->CargarObjeto("bola_azul.obj");
				equipo_azul->AsignarBola(6, bola);
				conjunto_bolas->NuevaBola(bola);
			}
	
}




/*
 *
 *	LÓGICA DEL JUEGO
 *
 *
 */
void Partida::ProcesarLogica()
{
	ElementoDinamico* bola = equipo_que_lanza->GetBolaSeleccionada();
	int nroParcial = juego_actual.parcialActual;
	pila_estados.pop();
	save_state ss = pila_estados.top();
	int box_seleccionado = equipo_que_lanza->GetBoxSeleccionado() + 1;
	equipo_que_lanza->LanzadaBola();
	
	if (bola->GetEquipo() == "blanca") // bolas blancas
	{
		// Si lanzamiento es válido
		if (bola->DentroCancha())
			LogicaSubBlancaValida(nroParcial);
		else // si no lo es
			LogicaSubBlancaInvalida(nroParcial);

	}
	else // Bolas de color
	{
	// Incrementamos, tanto si ha quedado fuera como si no, las bolas lanzadas en el parcial
		juego_actual.parcial[nroParcial].bolas_lanzadas++;
		// Si el lanzamiento es válido
		if (bola->DentroCancha())
			LogicaSubColorValido();
		else // Si no lo es válido el lanzamiento
		{
			// Internamente, la bola queda invisible y lejos, para no tocar colisionar con nada
			bola->Mover(-20, -20, -20);
			bola->SetVisibilidad(0);
			LogicaSubColorInvalido(nroParcial, box_seleccionado);
		}

		// COMPROBAR CONDICIONES ESPECIALES
		// QUE NINGUNA BOLA QUEDE FUERA (Tal vez al principio ? antes de la lógica de la bola?)
		int perdidas_azul = equipo_azul->BorraBolasFuera();
		juego_actual.parcial[nroParcial].perdidas_azul += perdidas_azul;

		int perdidas_rojo = equipo_rojo->BorraBolasFuera();
		juego_actual.parcial[nroParcial].perdidas_rojo += perdidas_rojo;
		
		hud->GetElemByID("bolas_azules")->SetImage(
				_imgLostBlue[juego_actual.parcial[nroParcial].perdidas_azul],
				150, 40, IMG_ALIGN_RIGHT);
		
		hud->GetElemByID("bolas_rojas")->SetImage(
				_imgLostRed[juego_actual.parcial[nroParcial].perdidas_rojo],
				150, 40, IMG_ALIGN_LEFT);
		
		// QUE LA BLANCA SIGA DENTRO CANCHA
		// Y LÓGICA FINALIZADA
		if (!juego_actual.parcial[nroParcial].bola_blanca->DentroCancha())
		{
			int posicion_adecuada = 0;
			float desplazamiento = 0.0;
			ElementoDinamico* blanca = juego_actual.parcial[nroParcial].bola_blanca;
			while (!posicion_adecuada)
			{
				blanca->Mover(2.48, 0.043, -6.176 + desplazamiento);

				if (conjunto_bolas->MasCercana()->Distancia(blanca) < 2 * 0.043)
				{
					desplazamiento += 0.01;
				}
				else
				{
					posicion_adecuada = 1;
				}
			}
		}
	}



	// Condición de final de parcial y/o juego y/o desempate
	if (!equipo_azul->MeQuedanBolas(-1) && !equipo_rojo->MeQuedanBolas(-1))
	{
		// Incrementar parciales, volver a arrancar equipos, y demás
		juego_actual.parcial[nroParcial].finalizado = 1;
		LogicaSubFinalComun(nroParcial);
		juego_actual.parcialActual++;

		// Se habrá finalizado el juego?
		if (juego_actual.nroParciales == juego_actual.parcialActual)
		{ 
			if (juego_actual.punt.rojo == juego_actual.punt.azul)
				LogicaSubIniciarDesempate ();
			else
				LogicaSubFinalJuego();
		}
		else	// Finalizamos y arrancamos otro parcial
			LogicaSubFinalParcial();
	}
	LogicaSubSetMenus(ss, nroParcial);
	CambiaEstado();
}







void Partida::LogicaSubBlancaValida(int nroParcial)
{
	juego_actual.parcial[
	    nroParcial
	    ].bola_blanca_en_campo = 1;
	juego_actual.parcial[
	    nroParcial
	    ].box_color = juego_actual.parcial[
		    nroParcial
		    ].box_bola_blanca;

	equipo_que_lanza->SetBox(juego_actual.parcial[
	    nroParcial
	    ].box_color);

}

void Partida::LogicaSubBlancaInvalida(int nroParcial)
{
	// Primero cambiar el equipo que lanza
	if (equipo_que_lanza->GetColor() == "rojo")
		equipo_que_lanza = equipo_azul;
	else 
		equipo_que_lanza = equipo_rojo;

	// Segundo elegir el box de lanzamiento
	int box_lanzamiento_nuevo = 
		juego_actual.parcial[
		    nroParcial
		    ].box_bola_blanca;
	box_lanzamiento_nuevo ++;
	
	

	switch (juego_actual.nroJugadores)
	{
		case 6:
			if (box_lanzamiento_nuevo > 6)
				box_lanzamiento_nuevo = 1;
			break;
		case 2:
			if (box_lanzamiento_nuevo > 4)
				box_lanzamiento_nuevo = 3;
			break;
	}
	// Volvemos a asignar el box de la bola blanca
	juego_actual.parcial[
	    nroParcial
	    ].box_bola_blanca = box_lanzamiento_nuevo;
	// Ahora asignamos el box elegido al equipo
	equipo_que_lanza->SetBox(box_lanzamiento_nuevo);
	// Y finalmente la bola
	equipo_que_lanza->SeleccionarBola(juego_actual.parcial[nroParcial].bola_blanca);
	juego_actual.parcial[nroParcial].bola_blanca->Reset();

	// Y ponemos la cámara donde tiene que estar
	pila_estados.top().box_camera = equipo_que_lanza->GetBoxSeleccionado() + 1;
	
	// Hay que setear el box aquí, porque se salta los bucles en los que se asignan las imágenes
	//  debido a que ni el box es nulo, ni la bola es nula (siempre la blanca)
	hud->GetElemByID("box")->SetImage(_imgBox[equipo_que_lanza->GetBoxSeleccionado()], 
	    100, 100, IMG_ALIGN_ABOVE);
	CambiaEstado ();

}

void Partida::LogicaSubColorValido()
{			
	// La bola ha quedado dentro, elegir si ha quedado más cerca de la blanca
	if (conjunto_bolas->MasCercana()->GetEquipo() == equipo_que_lanza->GetColor())
	{
		if (equipo_que_lanza->GetColor() == "rojo")
			if (equipo_azul->MeQuedanBolas(-1))
			equipo_que_lanza = equipo_azul;
		else 
			equipo_que_lanza = equipo_rojo;
		else if (equipo_que_lanza->GetColor() == "azul")
			if (equipo_rojo->MeQuedanBolas(-1))
			equipo_que_lanza = equipo_rojo;
		else
			equipo_que_lanza = equipo_azul;
	}
	else // mas lejos
	{
		// Se vuelve a elegir BOX

		if (equipo_que_lanza->GetColor() == "rojo")
			if (equipo_rojo->MeQuedanBolas(-1))
			equipo_que_lanza = equipo_rojo;
		else 
			equipo_que_lanza = equipo_azul;
		else if (equipo_que_lanza->GetColor() == "azul")
			if (equipo_azul->MeQuedanBolas(-1))
			equipo_que_lanza = equipo_azul;
		else
			equipo_que_lanza = equipo_rojo;
	} 
}

void Partida::LogicaSubColorInvalido(int nroParcial, int box_seleccionado)
{			
	// La bola ha quedado fuera, ha de tirar el del mismo box
	if (equipo_que_lanza->GetColor() == "rojo")
		juego_actual.parcial[nroParcial].perdidas_rojo++;
	else
		juego_actual.parcial[nroParcial].perdidas_azul++;

	hud->GetElemByID("bolas_rojas")->SetImage(
			_imgLostRed[juego_actual.parcial[nroParcial].perdidas_rojo],
	    	150, 40, IMG_ALIGN_LEFT);
	
	hud->GetElemByID("bolas_azules")->SetImage(
			_imgLostBlue[juego_actual.parcial[nroParcial].perdidas_azul],
	    	150, 40, IMG_ALIGN_RIGHT);

	if (equipo_que_lanza->MeQuedanBolas(box_seleccionado))
	{
		equipo_que_lanza->SetBox(box_seleccionado);
	}
	else if (!equipo_que_lanza->MeQuedanBolas(-1))
	{
		if (equipo_que_lanza->GetColor() == "rojo")
			equipo_que_lanza = equipo_azul;
		else equipo_que_lanza = equipo_rojo;
	}
}


void Partida::LogicaSubFinalComun(int nroParcial)
{		
	// Recuento de puntuaciones
	// Hacemos distancias a bolas y demás para el parcial
	std::string equipo_ganador = "Ningún equipo puntúa";
	if (conjunto_bolas->MasCercana() != NULL)
		equipo_ganador = conjunto_bolas->MasCercana()->GetEquipo();

	if (equipo_ganador == equipo_azul->GetColor())
	{
		ElementoDinamico* bola_blanca = juego_actual.parcial[
		    nroParcial
		    ].bola_blanca;
		float dist = equipo_rojo->MenorDistancia(bola_blanca);

		juego_actual.parcial[nroParcial].punt.azul = 
			equipo_azul->BolasPuntuables(dist, bola_blanca);
	}
	else if (equipo_ganador == equipo_rojo->GetColor())
	{
		ElementoDinamico* bola_blanca = juego_actual.parcial[
		    nroParcial
		    ].bola_blanca;
		float dist = equipo_azul->MenorDistancia(bola_blanca);

		juego_actual.parcial[nroParcial].punt.rojo = 
			equipo_rojo->BolasPuntuables(dist, bola_blanca);
	}

	//  y finalmente, sumamos el parcial a las puntuaciones globales del juego
	juego_actual.punt.rojo += juego_actual.parcial[
	    juego_actual.parcialActual
	    ].punt.rojo;

	juego_actual.punt.azul += juego_actual.parcial[
	    juego_actual.parcialActual
	    ].punt.azul;

	std::string punt = "Equipo rojo: ";
	char aux[10];
	sprintf(aux, "%i", juego_actual.punt.rojo);
	punt += aux;
	hud->GetElemByID("puntuacion_rojo")->SetText(punt);

	punt = "Equipo azul: ";
	sprintf(aux, "%i", juego_actual.punt.azul);
	punt += aux;
	hud->GetElemByID("puntuacion_azul")->SetText(punt);

	hud->GetElemByID("bolas_rojas")->SetImage(_imgLostRed[0], 150, 40);
	hud->GetElemByID("bolas_azules")->SetImage(_imgLostBlue[0], 150, 40);




}

void Partida::LogicaSubFinalParcial()
{			
	if (equipo_rojo != NULL)
		free (equipo_rojo);
	if (equipo_azul != NULL)
		free (equipo_azul);
	
	if (juego_actual.nroJugadores == 2)
	{
		equipo_rojo = new Equipo("rojo", 0);
		equipo_azul = new Equipo("azul", 0);
		equipo_rojo->SetBox(3);
		equipo_azul->SetBox(4);
	}
	else
	{
		equipo_rojo = new Equipo("rojo", 1);
		equipo_azul = new Equipo("azul", 1);
	}
	int box_bblanca = juego_actual.parcial[juego_actual.parcialActual].box_bola_blanca;

	if (box_bblanca == 1 || box_bblanca == 3 || box_bblanca == 5)
		equipo_que_lanza = equipo_rojo;
	else equipo_que_lanza = equipo_azul;


	equipo_que_lanza->SetBox(juego_actual.parcial[
	    juego_actual.parcialActual
	    ].box_bola_blanca);

	InicializaBolasEquipos();

	equipo_que_lanza->SeleccionarBola(juego_actual.parcial[
	    juego_actual.parcialActual
	    ].bola_blanca);

	pila_estados.top().box_camera = box_bblanca;


	hud->GetElemByID("bola")->SetImage(_imgWhiteBall, 100, 100, IMG_ALIGN_ABOVE);
	hud->GetElemByID("box")->SetImage(_imgBox[equipo_que_lanza->GetBoxSeleccionado()], 
	    100, 100, IMG_ALIGN_ABOVE);

	save_state ss = pila_estados.top();
	ss.estado = EST_MENU;
	ss.menu = espera_parcial;
	pila_estados.push(ss);
	PrepararMenuEsperaParcial(0);
}

void Partida::LogicaSubFinalJuego()
{				
	// Presentar menú
	// Juedo finalmente finalizado
	while (!pila_estados.empty())
	{
		save_state ss = pila_estados.top();
		if (ss.menu != NULL)
			if (ss.menu->GetID() == "principal")
		{
			CambiaEstado();
			break;
		}
		pila_estados.pop();
	}

	save_state ss = pila_estados.top();
	ss.estado = EST_MENU;
	ss.menu = espera_final;
	pila_estados.push(ss);
	PrepararMenuEsperaFinal();
}

void Partida::LogicaSubIniciarDesempate()
{
	// Guardamos los parciales antiguos
	parcial_boccia *viejos = new parcial_boccia[juego_actual.nroParciales];
	for (int i = 0; i < juego_actual.nroParciales; i++)
		viejos[i] = juego_actual.parcial[i];

	// Indicamos número de parciales de desempate y sumamos
	// un parcial al recuento de parciales de juego
	juego_actual.nroParciales++;
	juego_actual.nroParcialesDes++;

	// Redeclaramos la variable de los parciles
	delete[] juego_actual.parcial;
	juego_actual.parcial = new parcial_boccia[juego_actual.nroParciales];

	// Copiamos los viejos a los nuevos
	for (int i = 0; i < juego_actual.nroParciales - juego_actual.nroParcialesDes; i++)
		juego_actual.parcial[i] = viejos[i];

	// Inicializamos el último parcial a ceros y demás
	parcial_boccia par;
	par.bolas_lanzadas = 0;
	par.finalizado = 0;
	par.bola_blanca_en_campo = 0;
	par.box_bola_blanca = 0;
	par.box_color = 0;
	par.punt.azul = 2;
	par.punt.rojo = 0;
	par.bola_blanca = NULL;

	// Y lo añadimos
	juego_actual.parcial[juego_actual.nroParciales - 1] = par;

	// Elegir valores de lanzamiento boxes, demás, bola blanca

	if (equipo_rojo != NULL)
		free (equipo_rojo);
	if (equipo_azul != NULL)
		free (equipo_azul);
	
	
	if (juego_actual.nroJugadores == 2)
	{
		if (juego_actual.nroParciales % 2 == 1) 
			juego_actual.parcial[juego_actual.nroParciales - 1].box_bola_blanca = 3;
		else juego_actual.parcial[juego_actual.nroParciales - 1].box_bola_blanca = 4;

		equipo_rojo = new Equipo("rojo", 0);
		equipo_azul = new Equipo("azul", 0);
		equipo_rojo->SetBox(3);
		equipo_azul->SetBox(4);
	}
	else
	{

		juego_actual.parcial[juego_actual.nroParciales - 1].box_bola_blanca = juego_actual.nroParciales % 6;

		equipo_rojo = new Equipo("rojo", 1);
		equipo_azul = new Equipo("azul", 1);
	}
	int box_bblanca = juego_actual.parcial[juego_actual.nroParciales - 1].box_bola_blanca;

	if (box_bblanca == 1 || box_bblanca == 3 || box_bblanca == 5)
		equipo_que_lanza = equipo_rojo;
	else equipo_que_lanza = equipo_azul;


	equipo_que_lanza->SetBox(juego_actual.parcial[
	    juego_actual.parcialActual
	    ].box_bola_blanca);

	InicializaBolasEquipos();

	equipo_que_lanza->SeleccionarBola(juego_actual.parcial[
	    juego_actual.parcialActual
	    ].bola_blanca);

	pila_estados.top().box_camera = box_bblanca;



	hud->GetElemByID("bola")->SetImage(_imgWhiteBall, 100, 100, IMG_ALIGN_ABOVE);
	hud->GetElemByID("box")->SetImage(_imgBox[equipo_que_lanza->GetBoxSeleccionado()], 
	    100, 100, IMG_ALIGN_ABOVE);


	save_state ss = pila_estados.top();
	ss.estado = EST_MENU;
	ss.menu = espera_parcial;
	pila_estados.push(ss);
	PrepararMenuEsperaParcial(1);
}

void Partida::LogicaSubSetMenus(save_state ss, int nroParcial)
{
	// Camara por defecto en pila_estados.top().box_camera = camara que toca;
	// En caso de que se cambie de equipo 
	// Establecemos el equipo que lanza, etc.

	// hacemos Pop a la pila para volver al estado del juego
	// Camara por defecto en pila_estados.top().box_camera = camara que toca;
	// En caso de que se cambie de equipo 
	if (equipo_que_lanza->GetBoxSeleccionado() == -1 && !juego_actual.parcial[nroParcial].finalizado)
	{
		PrepararMenuBox();
		hud->GetElemByID("box")->SetImage(_imgUndetermined, 100, 100, IMG_ALIGN_ABOVE);
		hud->GetElemByID("bola")->SetImage(_imgUndetermined, 100, 100, IMG_ALIGN_ABOVE);
		ss.estado = EST_MENU;
		ss.menu = selbox;
		pila_estados.push(ss);
	}
	else if (equipo_que_lanza->GetBolaSeleccionada() == NULL && !juego_actual.parcial[nroParcial].finalizado)
	{
		hud->GetElemByID("box")->SetImage(_imgBox[equipo_que_lanza->GetBoxSeleccionado()], 100, 100, IMG_ALIGN_ABOVE);
		hud->GetElemByID("bola")->SetImage(_imgUndetermined, 100, 100, IMG_ALIGN_ABOVE);
		PrepararMenuBola();
		ss.estado = EST_MENU;
		ss.menu = selbola;
		pila_estados.push(ss);
	}
}